import io.kubernetes.client.openapi.ApiException;
import io.kubernetes.client.openapi.apis.*;
import io.kubernetes.client.openapi.models.*;
import io.kubernetes.client.util.Yaml;
import io.kubernetes.client.util.generic.options.DeleteOptions;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Map;

import static config.Constant.DEFAULT_NAMESPACE;
import static config.KubeConfigure.GetKubeClient;

public class Yaml2KubeObjectUtil {

    /**
     * create k8s object by yaml file
     *
     * @param yamlPath
     * @return
     */
    public static boolean CreateByYaml(String yamlPath) {
        try {
            return ConvertYaml2K8sObject(yamlPath, false);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * delete the k8s object by yaml file
     *
     * @param yamlPath
     * @return
     */
    public static boolean DeleteByYaml(String yamlPath) {
        try {
            return ConvertYaml2K8sObject(yamlPath, true);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }


    /**
     * read yaml file transform to k8s object
     *
     * @param yamlPath
     * @param delete
     * @return
     * @throws IOException
     * @throws ApiException
     */
    public static boolean ConvertYaml2K8sObject(String yamlPath, boolean delete) throws IOException, ApiException {
        String kindName = GetValueFromYamlFile("kind", yamlPath);
        GetKubeClient();
        switch (kindName) {
            case "Pod":
                return Pod2YamlK8sObject(delete, yamlPath, new CoreV1Api());
            case "Namespace":
                return NamespaceYaml2K8sObject(delete, yamlPath, new CoreV1Api());
            case "Deployment":
                return DeploymentYaml2K8sObject(delete, yamlPath, new AppsV1Api());
            case "ReplicaSet":
                return ReplicaSetYaml2K8sObject(delete, yamlPath, new AppsV1Api());
            case "ReplicationController":
                return ReplicaControllerYaml2K8sObject(delete, yamlPath, new CoreV1Api());
            case "StatefulSet":
                return StatefulSetYaml2K8sObject(delete, yamlPath, new AppsV1Api());
            case "DaemonSet":
                return DaemonSetYaml2K8sObject(delete, yamlPath, new AppsV1Api());
            case "Job":
                return JobYaml2K8sObject(delete, yamlPath, new BatchV1Api());
            case "CronJob":
                return CronJobYaml2K8sObject(delete, yamlPath, new BatchV1Api());
            case "Node":
                return NodeYaml2K8sObject(delete, yamlPath, new CoreV1Api());
            case "Service":
                return ServiceYaml2K8sObject(delete, yamlPath, new CoreV1Api());
            case "Secret":
                return SecretYaml2K8sObject(delete, yamlPath, new CoreV1Api());
            case "ConfigMap":
                return ConfigMapYaml2K8sObject(delete, yamlPath, new CoreV1Api());
            case "Ingress":
                return IngressYaml2K8sObject(delete, yamlPath, new NetworkingV1Api());
            case "ThirdPartyResource":
            case "ServiceAccount":
            case "StorageClass":
                return StorageClass2K8sObject(delete, yamlPath, new StorageV1Api());
            case "PersistentVolume":
                return PersistentVolume2K8sObject(delete, yamlPath, new CoreV1Api());
            case "PersistentVolumeClaim":
                return PersistentVolumeClaim2K8sObject(delete, yamlPath, new CoreV1Api());
            case "VolumeSnapshot":
            case "VolumeSnapshotContent":
            case "VolumeSnapshotClass":
            default:
                return false;
        }
    }

    /**
     * get namespace or return default namespace.
     *
     * @param namespace
     * @return namespace
     */
    public static String GetNamespaceOrDefault(String namespace) {
        if (namespace != null) return namespace;
        return DEFAULT_NAMESPACE;
    }

    /**
     * get value by key form yaml file
     *
     * @param key
     * @param filePath
     * @return value in yaml
     */
    public static String GetValueFromYamlFile(String key, String filePath) {
        try {
            org.yaml.snakeyaml.Yaml yaml = new org.yaml.snakeyaml.Yaml();
            File file = new File(filePath);
            Map map = (Map) yaml.load(new FileInputStream(file));
            return map.get(key).toString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "Pod";
    }

    /**
     * read pod yaml to k8s object
     *
     * @param delete,  delete or create
     * @param yamlPath
     * @param api,     k8s API
     * @return success or default
     * @throws IOException
     * @throws ApiException
     */
    public static boolean Pod2YamlK8sObject(boolean delete, String yamlPath, CoreV1Api api) throws IOException, ApiException {
        File file = new File(yamlPath);
        V1Pod yamlPod = (V1Pod) Yaml.load(file);
        String namespace = yamlPod.getMetadata().getNamespace();
        namespace = GetNamespaceOrDefault(namespace);
        if (delete) {
            V1Pod deleteResult =
                    api.deleteNamespacedPod(
                            yamlPod.getMetadata().getName(),
                            namespace,
                            null,
                            null,
                            null,
                            null,
                            null,
                            new V1DeleteOptions());
        } else {
            V1Pod createResult = api.createNamespacedPod(namespace, yamlPod, null,
                    null, null);
        }
        return true;
    }

    // read namespace yaml to k8s object
    public static boolean NamespaceYaml2K8sObject(boolean delete, String yamlPath, CoreV1Api api) throws ApiException, IOException {
        File file = new File(yamlPath);
        V1Namespace YamlNamespace = (V1Namespace) Yaml.load(file);
        String namespace = YamlNamespace.getMetadata().getNamespace();
        namespace = GetNamespaceOrDefault(namespace);
        if (delete) {
            api.deleteNamespace(namespace, null, null, null,
                    null, null, new V1DeleteOptions());
        } else {
            api.createNamespace(YamlNamespace, null, null, null);
        }
        return true;
    }

    // create the namespace without yaml
    public static boolean CreateNamespace(String namespace, boolean delete, CoreV1Api api) throws ApiException {
        V1Namespace v1Namespace = new V1Namespace();
        v1Namespace.setApiVersion("v1");
        v1Namespace.setKind("Namespace");
        V1ObjectMeta objectMeta = new V1ObjectMeta();
        objectMeta.setName(namespace);
        v1Namespace.setMetadata(objectMeta);
        if (delete) {
            api.deleteNamespace(namespace, null, null, null, null,
                    null, new V1DeleteOptions());
        } else {
            api.createNamespace(v1Namespace, null, null, null);
        }
        return true;
    }

    // read deployment yaml to k8s object
    public static boolean DeploymentYaml2K8sObject(boolean delete, String yamlPath, AppsV1Api api) throws ApiException, IOException {
        File file = new File(yamlPath);
        V1Deployment yamlDeployment = (V1Deployment) Yaml.load(file);
        String namespace = yamlDeployment.getMetadata().getNamespace();
        namespace = GetNamespaceOrDefault(namespace);
        if (delete) {
            api.deleteNamespacedDeployment(yamlDeployment.getMetadata().getName(),
                    namespace, null, null, null,
                    null, null, new V1DeleteOptions());
        } else {
            api.createNamespacedDeployment(namespace, yamlDeployment, null, null, null);
        }
        return true;
    }

    // read replicaset yaml to k8s object
    public static boolean ReplicaSetYaml2K8sObject(boolean delete, String yamlPath, AppsV1Api api) throws ApiException, IOException {
        File file = new File(yamlPath);
        V1ReplicaSet yamlReplicaSet = (V1ReplicaSet) Yaml.load(file);
        String namespace = yamlReplicaSet.getMetadata().getNamespace();
        namespace = GetNamespaceOrDefault(namespace);
        if (delete) {
            api.deleteNamespacedReplicaSet(yamlReplicaSet.getMetadata().getName(),
                    namespace, null, null, null,
                    null, null, new V1DeleteOptions());
        } else {
            api.createNamespacedReplicaSet(namespace, yamlReplicaSet, null, null, null);
        }
        return true;
    }

    // read replica controller yaml to k8s object
    // should change replica = 0 before delete
    public static boolean ReplicaControllerYaml2K8sObject(boolean delete, String yamlPath, CoreV1Api api) throws ApiException, IOException {
        File file = new File(yamlPath);
        V1ReplicationController yamlReplicaController = (V1ReplicationController) Yaml.load(file);
        String namespace = yamlReplicaController.getMetadata().getNamespace();
        namespace = GetNamespaceOrDefault(namespace);
        if (delete) {
            api.deleteNamespacedReplicationController(yamlReplicaController.getMetadata().getName(),
                    namespace, null, null, null,
                    null, null, new V1DeleteOptions());
        } else {
            api.createNamespacedReplicationController(namespace, yamlReplicaController, null, null, null);
        }
        return true;
    }

    // read StatefulSet yaml to k8s object
    public static boolean StatefulSetYaml2K8sObject(boolean delete, String yamlPath, AppsV1Api api) throws ApiException, IOException {
        File file = new File(yamlPath);
        V1StatefulSet yamlStatefulSet = (V1StatefulSet) Yaml.load(file);
        String namespace = yamlStatefulSet.getMetadata().getNamespace();
        namespace = GetNamespaceOrDefault(namespace);
        if (delete) {
            api.deleteNamespacedStatefulSet(yamlStatefulSet.getMetadata().getName(),
                    namespace, null, null, null,
                    null, null, new V1DeleteOptions());
        } else {
            api.createNamespacedStatefulSet(namespace, yamlStatefulSet, null, null, null);
        }
        return true;
    }

    // read DaemonSet yaml to k8s object
    public static boolean DaemonSetYaml2K8sObject(boolean delete, String yamlPath, AppsV1Api api) throws ApiException, IOException {
        File file = new File(yamlPath);
        V1DaemonSet yamlDaemonSet = (V1DaemonSet) Yaml.load(file);
        String namespace = yamlDaemonSet.getMetadata().getNamespace();
        namespace = GetNamespaceOrDefault(namespace);
        if (delete) {
            api.deleteNamespacedDaemonSet(yamlDaemonSet.getMetadata().getName(),
                    namespace, null, null, null,
                    null, null, new V1DeleteOptions());
        } else {
            api.createNamespacedDaemonSet(namespace, yamlDaemonSet, null, null, null);
        }
        return true;
    }

    // read Job yaml to k8s object
    public static boolean JobYaml2K8sObject(boolean delete, String yamlPath, BatchV1Api api) throws ApiException, IOException {
        File file = new File(yamlPath);
        V1Job yamlJob = (V1Job) Yaml.load(file);
        String namespace = yamlJob.getMetadata().getNamespace();
        namespace = GetNamespaceOrDefault(namespace);
        if (delete) {
            api.deleteNamespacedJob(yamlJob.getMetadata().getName(),
                    namespace, null, null, null,
                    null, null, new V1DeleteOptions());
        } else {
            api.createNamespacedJob(namespace, yamlJob, null, null, null);
        }
        return true;
    }

    // read CronJob yaml to k8s object
    // make sure the k8s version higher than 1.21.0
    public static boolean CronJobYaml2K8sObject(boolean delete, String yamlPath, BatchV1Api api) throws ApiException, IOException {
        File file = new File(yamlPath);
        V1CronJob yamlCronJob = (V1CronJob) Yaml.load(file);
        String namespace = yamlCronJob.getMetadata().getNamespace();
        namespace = GetNamespaceOrDefault(namespace);
        if (delete) {
            api.deleteNamespacedCronJob(yamlCronJob.getMetadata().getName(),
                    namespace, null, null, null,
                    null, null, new V1DeleteOptions());
        } else {
            api.createNamespacedCronJob(namespace, yamlCronJob, null, null, null);
        }
        return true;
    }

    // read node yaml to k8s object
    public static boolean NodeYaml2K8sObject(boolean delete, String yamlPath, CoreV1Api api) throws ApiException, IOException {
        File file = new File(yamlPath);
        V1Node YamlNode = (V1Node) Yaml.load(file);
        String namespace = YamlNode.getMetadata().getNamespace();
        namespace = GetNamespaceOrDefault(namespace);
        if (delete) {
            api.deleteNode(YamlNode.getMetadata().getName(), null, null, null,
                    null, null, new V1DeleteOptions());
        } else {
            api.createNode(YamlNode, null, null, null);
        }
        return true;
    }

    // read service yaml to k8s object
    public static boolean ServiceYaml2K8sObject(boolean delete, String yamlPath, CoreV1Api api) throws ApiException, IOException {
        File file = new File(yamlPath);
        V1Service YamlService = (V1Service) Yaml.load(file);
        String namespace = YamlService.getMetadata().getNamespace();
        namespace = GetNamespaceOrDefault(namespace);
        if (delete) {
            api.deleteNamespacedService(YamlService.getMetadata().getName(), namespace, null, null,
                    null, null, null, new DeleteOptions());
        } else {
            api.createNamespacedService(namespace, YamlService, null, null, null);
        }
        return true;
    }

    // read Secret yaml to k8s object
    public static boolean SecretYaml2K8sObject(boolean delete, String yamlPath, CoreV1Api api) throws ApiException, IOException {
        File file = new File(yamlPath);
        V1Secret YamlSecret = (V1Secret) Yaml.load(file);
        String namespace = YamlSecret.getMetadata().getNamespace();
        namespace = GetNamespaceOrDefault(namespace);
        if (delete) {
            api.deleteNamespacedSecret(YamlSecret.getMetadata().getName(), namespace, null, null,
                    null, null, null, new DeleteOptions());
        } else {
            api.createNamespacedSecret(namespace, YamlSecret, null, null, null);
        }
        return true;
    }

    // read ConfigMap yaml to k8s object
    public static boolean ConfigMapYaml2K8sObject(boolean delete, String yamlPath, CoreV1Api api) throws ApiException, IOException {
        File file = new File(yamlPath);
        V1ConfigMap YamlConfigMap = (V1ConfigMap) Yaml.load(file);
        String namespace = YamlConfigMap.getMetadata().getNamespace();
        namespace = GetNamespaceOrDefault(namespace);
        if (delete) {
            api.deleteNamespacedConfigMap(YamlConfigMap.getMetadata().getName(), namespace, null, null,
                    null, null, null, new DeleteOptions());
        } else {
            api.createNamespacedConfigMap(namespace, YamlConfigMap, null, null, null);
        }
        return true;
    }

    // read Ingress yaml to k8s object
    // unworked, check api version
    public static boolean IngressYaml2K8sObject(boolean delete, String yamlPath, NetworkingV1Api api) throws ApiException, IOException {
        File file = new File(yamlPath);
        V1Ingress YamlIngress = (V1Ingress) Yaml.load(file);
        String namespace = YamlIngress.getMetadata().getNamespace();
        namespace = GetNamespaceOrDefault(namespace);
        if (delete) {
            api.deleteNamespacedIngress(YamlIngress.getMetadata().getName(), namespace, null, null,
                    null, null, null, new DeleteOptions());
        } else {
            api.createNamespacedIngress(namespace, YamlIngress, null, null, null);
        }
        return true;
    }


    // read StorageClass yaml to k8s object
    public static boolean StorageClass2K8sObject(boolean delete, String yamlPath, StorageV1Api api) throws ApiException, IOException {
        File file = new File(yamlPath);
        V1StorageClass YamlStorageClass = (V1StorageClass) Yaml.load(file);
        if (delete) {
            api.deleteStorageClass(YamlStorageClass.getMetadata().getName(), null, null,
                    null, null, null, new DeleteOptions());
        } else {
            api.createStorageClass(YamlStorageClass, null, null, null);
        }
        return true;
    }

    // read PersistentVolume yaml to k8s object
    public static boolean PersistentVolume2K8sObject(boolean delete, String yamlPath, CoreV1Api api) throws ApiException, IOException {
        File file = new File(yamlPath);
        V1PersistentVolume YamlPersistentVolume = (V1PersistentVolume) Yaml.load(file);
        if (delete) {
            api.deletePersistentVolume(YamlPersistentVolume.getMetadata().getName(), null, null,
                    null, null, null, new DeleteOptions());
        } else {
            api.createPersistentVolume(YamlPersistentVolume, null, null, null);
        }
        return true;
    }

    // read PersistentVolumeClaim yaml to k8s object
    public static boolean PersistentVolumeClaim2K8sObject(boolean delete, String yamlPath, CoreV1Api api) throws ApiException, IOException {
        File file = new File(yamlPath);
        V1PersistentVolumeClaim YamlPersistentVolume = (V1PersistentVolumeClaim) Yaml.load(file);
        String namespace = YamlPersistentVolume.getMetadata().getNamespace();
        namespace = GetNamespaceOrDefault(namespace);
        if (delete) {
            api.deleteNamespacedPersistentVolumeClaim(YamlPersistentVolume.getMetadata().getName(), namespace, null, null,
                    null, null, null, new DeleteOptions());
        } else {
            api.createNamespacedPersistentVolumeClaim(namespace, YamlPersistentVolume, null, null, null);
        }
        return true;
    }

}
